﻿using System;
using System.Collections;
using System.Diagnostics;
using System.DirectoryServices;
using System.Management;
using System.Net;
using System.ServiceProcess;
using Microsoft.Web.Administration;
using Microsoft.Win32;

namespace harbar.net.Utils.APM
{
    internal static class Helper
    {
        #region Private Properties 
        private static string _iisRegKey = @"Software\Microsoft\InetStp";
        private static string _metabasePath = @"IIS://localhost/w3svc/apppools";
        private static string _stsadmPath = Environment.GetEnvironmentVariable("commonprogramfiles") + @"\Microsoft Shared\web server extensions\12\BIN\stsadm";
        private static string _wmiQuery = @"SELECT * FROM Win32_Process WHERE Name = 'w3wp.exe'";
        #endregion

        #region Public Methods

        // detects IIS6 and above
        public static bool IisInstalled()
        {
            try
            {
                using (RegistryKey iisKey = Registry.LocalMachine.OpenSubKey(_iisRegKey))
                {
                    return (int)iisKey.GetValue("MajorVersion") >= 6;
                }
            }
            catch 
            {
                return false;
            }
        }

        // returns status of web service
        public static string W3svcStatus()
        {
            try
            {
                using (ServiceController serviceController = new ServiceController("w3svc"))
                {
                    return serviceController.Status.ToString();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // shim to prevent Assembly load failure on Windows 2003
        public static AppPools GetAppPools()
        {
            return IsIIS7() ? GetAppPoolsIIS7() : GetAppPoolsIIS6();
        }

        // shim to prevent Assembly load failure on Windows 2003
        public static ArrayList GetAppPoolNames()
        {
            return IsIIS7() ? GetAppPoolNamesIIS7() : GetAppPoolNamesIIS6();
        }

        // shim to prevent Assembly load failure on Windows 2003
        public static void RecycleAppPool(string name)
        {
            if (IsIIS7())
            {
                RecycleAppPoolIIS7(name);
            }
            else
            {
                RecycleAppPoolIIS6(name);
            }
        }

        // restarts an NT service using it's short name
        public static void RestartService(string name)
        {
            try
            {
                using (ServiceController serviceController = new ServiceController(name))
                {
                    serviceController.Stop();
                    serviceController.WaitForStatus(ServiceControllerStatus.Stopped);
                    serviceController.Start();
                    serviceController.WaitForStatus(ServiceControllerStatus.Running);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // executes iisreset.exe
        public static void IisReset()
        {
            try
            {
                using (Process p = new Process())
                {
                    p.StartInfo.FileName = "iisreset.exe";
                    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                    p.Start();
                    p.WaitForExit();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // executes stsadm.exe with no arguments
        public static void ExecuteStsAdm()
        {
            try
            {
                using (Process p = new Process())
                {
                    p.StartInfo.FileName = _stsadmPath;
                    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                    p.Start();
                    p.WaitForExit();
                }
            }
            catch (Exception ex)
            {
                if (ex.Message == "The system cannot find the file specified")
                {
                    throw new Exception("Could not find STSADM, SharePoint may not be installed.");
                }
                throw ex;
            }
        }

        // performs a HTTP GET for a given URL
        public static void HttpGet(string url)
        {
            try
            {
                WebRequest request = WebRequest.Create(url);
                request.Credentials = CredentialCache.DefaultCredentials;
                request.Method = "GET";
                using (WebResponse response = request.GetResponse())
                {
                    if (response.ContentLength <= 0)
                    {
                        throw new Exception("No content at " + url);
                    }
                    response.Close();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // writes an application event log
        public static void WriteEvent(Exception ex)
        {
            string source = "APM2";
            string log = "Application";
            string entry = "An error occured in Application Pool Manager.\r\n\r\nMessage:\r\n" + ex.Message + "\r\n\r\nStackTrace:\r\n" + ex.StackTrace;
            if (!EventLog.SourceExists(source))
            {
                EventLog.CreateEventSource(source, log);
            }
            EventLog.WriteEntry(source, entry, EventLogEntryType.Error);
        }

        #endregion

        #region Private Methods

        // determines if IIS7 is installed
        private static bool IsIIS7()
        {
            try
            {
                using (RegistryKey iisKey = Registry.LocalMachine.OpenSubKey(_iisRegKey))
                {
                    return (int)iisKey.GetValue("MajorVersion") == 7;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // uses WMI to get Process IDs (for legacy IIS6)
        private static string GetProcessIDs(string appPoolName)
        {
            string ret = null;
            try
            {
                using (ManagementObjectSearcher mos = new ManagementObjectSearcher("root\\CIMV2", _wmiQuery))
                {
                    foreach (ManagementObject mo in mos.Get())
                    {
                        if (mo["CommandLine"].ToString().Contains(appPoolName))
                        {
                            ret += mo["ProcessId"].ToString() + ",";
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ret = null;
                throw ex;
            }
            return ret;
        }

        // translates state into string (for legacy IIS6)
        private static string GetAppPoolState(int state)
        {
            string ret = null;
            try
            {
                switch (state)
                {
                    case 1:
                        ret = "Starting";
                        break;
                    case 2:
                        ret = "Started";
                        break;
                    case 3:
                        ret = "Stopping";
                        break;
                    case 0:
                        ret = "Stopped";
                        break;
                    default:
                        ret = "Unknown";
                        break;
                }
            }
            catch (Exception ex)
            {
                ret = null;
                throw ex;
            }
            return ret;
        }

        // enumerates App Pools (using legacy IIS6 metabase)
        private static ArrayList GetAppPoolNamesIIS6()
        {
            ArrayList ret = new ArrayList();
            try 
            {
                using (DirectoryEntry appPools = new DirectoryEntry(_metabasePath))
                {
                    foreach (DirectoryEntry ap in appPools.Children)
                    {
                        ret.Add(ap.Name);
                        ap.Dispose();
                    }
                }
            }
            catch (Exception ex)
            {
                ret = null;
                throw ex;
            }
            return ret;
        }

        // enumerates App Pools (using Microsoft.Web.Administration)
        private static ArrayList GetAppPoolNamesIIS7()
        {
            ArrayList ret = new ArrayList();
            try 
            {
                using (ServerManager serverManager = new ServerManager())
                {
                    foreach (ApplicationPool ap in serverManager.ApplicationPools)
                    {
                        ret.Add(ap.Name);
                    }
                }
            }
            catch (Exception ex)
            {
                ret = null;
                throw ex;
            }
            return ret;
        }

        // enumerates App Pools and properties (using legacy IIS6 metabase)
        private static AppPools GetAppPoolsIIS6()
        {
            AppPools ret = new AppPools();
            try
            {
                using (DirectoryEntry appPools = new DirectoryEntry(_metabasePath))
                {
                    foreach (DirectoryEntry ap in appPools.Children)
                    {
                        AppPool item = new AppPool();
                        item.Name = ap.Name;
                        item.Status = GetAppPoolState((int)ap.Properties["AppPoolState"].Value);
                        item.Pids = GetProcessIDs(ap.Name);
                        ap.Dispose();
                        ret.Add(item);
                    }
                }
            }
            catch (Exception ex)
            {
                ret = null;
                throw ex;
            }
            return ret;
        }

        // enumerates App Pools and properties (using Microsoft.Web.Administration)
        private static AppPools GetAppPoolsIIS7()
        {
            AppPools ret = new AppPools();
            try
            {
                using (ServerManager serverManager = new ServerManager())
                {
                    foreach (ApplicationPool ap in serverManager.ApplicationPools)
                    {
                        AppPool item = new AppPool();
                        item.Name = ap.Name;
                        item.Status = ap.State.ToString();
                        foreach (WorkerProcess wp in ap.WorkerProcesses)
                        {
                            item.Pids += wp.ProcessId + ",";
                        }
                        ret.Add(item);
                    }
                }
            }
            catch (Exception ex)
            {
                ret = null;
                throw ex;
            }
            return ret;
        }

        // recylces app pool (using legacy IIS6 metabase)
        private static void RecycleAppPoolIIS6(string name)
        {
            try
            {
                using (DirectoryEntry appPool = new DirectoryEntry(string.Format(_metabasePath + @"/{0}", name)))
                {
                    appPool.Invoke("Recycle");
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // recylces app pool (using Microsoft.Web.Administration)
        private static void RecycleAppPoolIIS7(string name)
        {
            try
            {
                using (ServerManager serverManager = new ServerManager())
                {
                    ApplicationPool ap = serverManager.ApplicationPools[name];
                    ap.Recycle();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion   
    }

    #region AppPool Wrappers

    internal class AppPool
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        private string _status;
        public string Status
        {
            get { return _status; }
            set { _status = value; }
        }

        private string _pids;
        public string Pids
        {
            get { return _pids; }
            set { _pids = value; }
        }


    }

    internal class AppPools : System.Collections.CollectionBase
    {
        public int Add(AppPool item)
        {
            return List.Add(item);
        }
        public void Insert(int index, AppPool item)
        {
            List.Insert(index, item);
        }
        public void Remove(AppPool item)
        {
            List.Remove(item);
        }
        public bool Contains(AppPool item)
        {
            return List.Contains(item);
        }
        public int IndexOf(AppPool item)
        {
            return List.IndexOf(item);
        }
        public void CopyTo(AppPool[] array, int index)
        {
            List.CopyTo(array, index);
        }
        public AppPool this[int index]
        {
            get { return (AppPool)List[index]; }
            set { List[index] = value; }
        }

    }

    #endregion
}
